﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace King.Extensions {

    public interface IEnumerableAsync : IEnumerable {
        IEnumeratorAsync GetEnumerator();
    }
    public interface IEnumerableAsync<T> : IEnumerableAsync, IEnumerable<T> {
        IEnumeratorAsync<T> GetEnumerator();
    }
    public interface IEnumeratorAsync : IEnumerator {
        Task<bool> MoveNextAsync();
    }
    public interface IEnumeratorAsync<T> : IEnumerator<T>, IEnumeratorAsync {
    }
    public interface IStreamableAsync<T> : IEnumerableAsync<T>, IEnumeratorAsync<T> {
    }

    public class EnumerableAsync : IEnumerableAsync {
        public IEnumerable m_enumerable;
        public EnumerableAsync(IEnumerable enumerable) {
            m_enumerable = enumerable;
        }

        public IEnumeratorAsync GetEnumerator() {
            return new EnumeratorAsync(m_enumerable.GetEnumerator());
        }
        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
    }
    public class EnumerableAsync<T> : EnumerableAsync, IEnumerableAsync<T> {
        public EnumerableAsync(IEnumerable<T> enumerable)
            : base(enumerable) {
        }
        
        public new IEnumeratorAsync<T> GetEnumerator() {
            return new EnumeratorAsync<T>(m_enumerable.GetEnumerator());
        }
        IEnumerator<T> IEnumerable<T>.GetEnumerator() {
            return GetEnumerator();
        }
        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }
    }

    /// <summary>
    /// Implements MoveNextAsync given an enumerator that returns a mix of 
    /// actual results and Task<bool> which come wrapped in MoveNextResult.
    /// This allows implementation of the enumerator with a continuation and then
    /// wrapping the result with EnumeratorAsync to implement MoveNextAsync. 
    /// MoveNextAsync awaits the Task inside of any MoveNextResults it encounters 
    /// and returns the result.
    /// </summary>
    public class EnumeratorAsync : IEnumeratorAsync {
        public struct MoveNextResult {
            public static explicit operator MoveNextResult(Task<bool> task) {
                return new MoveNextResult(task);
            }

            private Task<bool> m_task;
            internal MoveNextResult(Task<bool> task) {
                m_task = task;
            }
            internal Task<bool> Task { get { return m_task; } }
        }

        private IEnumerator m_enumerator;

        public EnumeratorAsync(IEnumerator enumerator) {
            m_enumerator = enumerator;
        }

        public object Current {
            get { return m_enumerator.Current; }
        }
        public async Task<bool> MoveNextAsync() {

            while (true) {
                if (!m_enumerator.MoveNext())
                    return false;

                var current = m_enumerator.Current;
                if (!(current is MoveNextResult))
                    break;

                var moveNext = await ((MoveNextResult)current).Task;
                if (!moveNext)
                    return false;
            }

            return true;
        }
        public bool MoveNext() {
            var task = MoveNextAsync();
            task.Wait();
            return task.Result;
        }
        public void Reset() {
            m_enumerator.Reset();
        }
        public void Dispose() {
            var objectEnumerator = m_enumerator as IEnumerator<object>;
            if (objectEnumerator == null)
                return;

            objectEnumerator.Dispose();
        }
    }
    public class EnumeratorAsync<T> : EnumeratorAsync, IEnumeratorAsync<T> {
        public EnumeratorAsync(IEnumerator enumerator)
            : base(enumerator) {
        }
        public new T Current {
            get { return (T)base.Current; }
        }
    }
}
